﻿using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;

namespace Czf_Serialize
{
	public class CzfStreamWriter:BinaryWriter
	{
		public event EventException OnError;
		private Object obj;
		private int zipLv = 0;

		public CzfStreamWriter ():base(new MemoryStream())
		{
		}

		public CzfStreamWriter (int zipLv):base(new MemoryStream())
		{
			this.zipLv = zipLv;
		}

		public byte[] ObjectToBytes(Object obj)
		{
			this.obj = obj;
			this.BaseStream.Position = 0;
			WriteObject (obj);
			var ret = ((MemoryStream)this.BaseStream).ToArray ();
			if (zipLv == 0) {
				return ret;
			} else {
				return CzfZipHelper.Zip(ret);
			}
		}

        void WriteInt16L(short val)
        {
            if (CzfStreamReader.heightInt)
            {
                Write(val);
            } else {
                Write((byte)(val >> 8));
                Write((byte)(val));
            }
        }

        void WriteInt32L(int val)
        {
            if (CzfStreamReader.heightInt)
            {
                Write(val);
            }
            else
            {
                Write((byte)(val >> 24));
                Write((byte)(val >> 16));
                Write((byte)(val >> 8));
                Write((byte)(val));
            }
        }

        void WriteUInt32L(uint val)
        {
            if (CzfStreamReader.heightInt)
            {
                Write(val);
            }
            else
            {
                Write((byte)(val >> 24));
                Write((byte)(val >> 16));
                Write((byte)(val >> 8));
                Write((byte)(val));
            }
        }

        void WriteInt64L(long val)
        {
            if (CzfStreamReader.heightInt)
            {
                Write(val);
            }
            else
            {
                Write((byte)(val >> 56));
                Write((byte)(val >> 48));
                Write((byte)(val >> 40));
                Write((byte)(val >> 32));
                Write((byte)(val >> 24));
                Write((byte)(val >> 16));
                Write((byte)(val >> 8));
                Write((byte)(val));
            }
        }

		private void WriteTypeTag(Type type){
			if (type == typeof(Byte)) {
				Write ( (byte)CzfSerializeType.Byte);
			} else if (type == typeof(Boolean)) {
				Write ( (byte)CzfSerializeType.Boolean);
			} else if (type == typeof(Int16)) {
				Write ( (byte)CzfSerializeType.Int16);
			} else if (type == typeof(Int32)) {
				Write ( (byte)CzfSerializeType.Int32);
			} else if (type == typeof(UInt32)) {
				Write ( (byte)CzfSerializeType.UInt32);
			} else if (type == typeof(Int64)) {
				Write ( (byte)CzfSerializeType.Int64);
			} else if (type == typeof(float)) {
				Write ( (byte)CzfSerializeType.Float);
			//} else if (type == typeof(double)) {
				//Write ( (byte)CzfSerializeType.Double);
			} else if (type == typeof(string)) {
				Write ( (byte)CzfSerializeType.String);
			} else if (type == typeof(DateTime)) {
				Write ( (byte)CzfSerializeType.DateTime);
			} else if (type.IsArray) {
				Write ( (byte)CzfSerializeType.Array);
            } else if (typeof(IList).IsAssignableFrom(type) && type.IsGenericType) {
                Write((byte)CzfSerializeType.Array);
            } else if (typeof(IDictionary).IsAssignableFrom(type) && type.IsGenericType) {
                Write((byte)CzfSerializeType.Hashtable);
			//} else if (typeof(IList).IsAssignableFrom(type) && type.IsGenericType) {
			//	Write ((byte)CzfSerializeType.GenericList);
			//	Type t = type.GetGenericArguments () [0];
			//	WriteTypeTag (t);
			//} else if (typeof(IDictionary).IsAssignableFrom(type) && type.IsGenericType) {
				//Write ( (byte)CzfSerializeType.GenericDictionary);
				//WriteTypeTag (type.GetGenericArguments () [0]);
				//WriteTypeTag (type.GetGenericArguments () [1]);
            }else if (type == typeof(Hashtable)) {
				Write ( (byte)CzfSerializeType.Hashtable);
			//} else if (type.IsClass) {
				//Write ( (byte)CzfSerializeType.Class);
			}
			else {
				Exception e = new Exception ("undefined serialize type:" + type.Name + " obj:" + obj+"\n this.obj="+this.obj);
				if (OnError != null)
					OnError (e);
				throw e;
			}
		}

		public void WriteObject(Object obj)
		{
			if (obj == null || obj is DBNull) {
				Write((byte)CzfSerializeType.Null);
				return;
			}
			
			Type type = obj.GetType ();
			WriteTypeTag (type);

			if (type == typeof(Byte)) {
				Write ((Byte)obj);
			} else if (type == typeof(Boolean)) {
				Write ((Boolean)obj);
			} else if (type == typeof(Int16)) {
                WriteInt16L ((Int16)obj);
			} else if (type == typeof(Int32)) {
                WriteInt32L ((Int32)obj);
			} else if (type == typeof(UInt32)) {
                WriteUInt32L ((UInt32)obj);
			} else if (type == typeof(Int64)) {
                WriteInt64L ((Int64)obj);
			} else if (type == typeof(float)) {
				Write ((float)obj);
			} else if (type == typeof(double)) {
				Write ((double)obj);
			} else if (type == typeof(string)) {
				WriteString ((string)obj);
			} else if (type == typeof(DateTime)) {
				Write (((DateTime)obj).Ticks);
			} else if (type.IsArray) {
				Object[] array = (Object[])obj;
                WriteInt32L (array.Length);
				for (int i = 0; i < array.Length; i++) {
					WriteObject (array [i]);
				}
			} else if (obj is IList) {
				IList array = (IList)obj;
                WriteInt32L (array.Count);
				for (int i = 0; i < array.Count; i++) {
					WriteObject (array [i]);
				}
			} else if (obj is IDictionary) {
				IDictionary dict = (IDictionary)obj;
                WriteInt32L (dict.Count);
				foreach (Object key in dict.Keys) {
					WriteObject (key);
					WriteObject (dict [key]);
				}
			}else if (obj is Hashtable) {
				Hashtable dict = (Hashtable)obj;
                WriteInt32L (dict.Count);
				foreach (Object key in dict.Keys) {
					WriteObject (key);
					WriteObject (dict [key]);
				}
			} else if (type.IsClass) {
				WriteClass (obj);
			}
			else {
				Exception e = new Exception ("undefined serialize type:" + type.Name + " obj:" + obj+"\n this.obj="+this.obj);
				if (OnError != null)
					OnError (e);
				throw e;
			}
		}

		private void WriteString(string str){
			byte[] bytes = System.Text.UnicodeEncoding.UTF8.GetBytes (str);
            var len = bytes.Length;
            if (len > short.MaxValue)
            {
                throw new Exception("字符串长度超长:" + len);
            }
            WriteInt16L ((short)len);
			WriteBytes (bytes);
		}

		private void WriteClass(Object obj){
			Type type = obj.GetType ();
			WriteString (type.Assembly.FullName);
			WriteString (type.FullName);
			FieldInfo[] fieldinfos = type.GetFields ();
            WriteInt32L (fieldinfos.Length);
			foreach (FieldInfo field in fieldinfos) {
				WriteString (field.Name);
				WriteObject (field.GetValue (obj));
			}
		}

		private void WriteBytes(byte[] bytes)
		{
			this.BaseStream.Write (bytes, 0, bytes.Length);
		}
	}
}

